﻿using System.Xml;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Threading;
using System.Text.RegularExpressions;
using AndBurn.DDSReader;
using DnPakExplorer.Forms;

namespace DnPakExplorer {
	public class Util {

		static PakExplorer mPakExplorer;
		static Hashtable mBufferdDntables;
		static Dntable mExchangeTable;
		static List<int> mJobParent;
		static Color[] mCharToColor;
		static Image mJobIconPvp;

		public static Regex StateVlauePettern = new Regex(@"-?\d*\.?\d+$");

		static Util() {
			for (int i=0; i<stringBuff.Length; i++) {
				stringBuff[i] = "";
			}
			mCharToColor = new Color['z'-'a'+1];
			mCharToColor['y'-'a'] = Color.Yellow;
			mCharToColor['r'-'a'] = Color.Red;
			mCharToColor['w'-'a'] = Color.White;
			mCharToColor['v'-'a'] = Color.Violet;
			mCharToColor['b'-'a'] = Color.Blue;
			mCharToColor['g'-'a'] = Color.Green;
			mCharToColor['h'-'a'] = Color.Honeydew;
			mCharToColor['s'-'a'] = Color.SkyBlue;
			mCharToColor['j'-'a'] = Color.Chocolate;
			mCharToColor['p'-'a'] = Color.PaleVioletRed;
			mCharToColor['l'-'a'] = Color.LightGray;
			mCharToColor['o'-'a'] = Color.Orange;
		}

		public static void SetExplorer(PakExplorer explorer) {
			mPakExplorer = explorer;
			mBufferdDntables = new Hashtable();
		}

		public static PakExplorer GetExplorer() {
			return mPakExplorer;
		}

		public static Color CharToColor(char c) {
			if (c>='a' && c<='z')
				return mCharToColor[c-'a'];
			return Color.Empty;
		}

		public static int Clamp(int value, int min, int max) {
			return value<min ? min : value>max? max : value;
		}

		const long ONE_KB = 1024;
		const long ONE_MB = 1024*ONE_KB;
		const long ONE_GB = 1024*ONE_MB;
		public static string FileSizeToString(long size) {
			if (size < ONE_KB) {
				return size+" B";
			} else if (size < ONE_MB) {
				return size*100/ONE_KB/100d + " KB";
			} else if (size < ONE_GB) {
				return size*100/ONE_MB/100d + " MB";
			} else {
				return size*100/ONE_GB/100d + " GB";
			}
		}

		public static string DuriationMillToString(int t) {
			string s;
			if (t < 1000) {
				s = t+"毫秒";
			} else if (t < 60000) {
				s = t/1000 + "秒";
				t = t%1000;
				if (t != 0) 
					s = s + DuriationMillToString(t);
			} else if (t < 3600000) {
				s = t/60000 + "分";
				t = t%60000;
				if (t != 0)
					s = s + DuriationMillToString(t);
			} else if (t < 86400000) {
				s = t/3600000 + "小时";
				t = t%3600000;
				if (t != 0)
					s = s + DuriationMillToString(t);
			} else {
				s = t/86400000 + "天";
				t = t%86400000;
				if (t != 0)
					s = s + DuriationMillToString(t);
			}
			return s;
		}

		public static string GetJobName(int jobId){
			if (jobId == 0) {
				return "无限制";
			} else {
				string name = null;
				for (int r=0; r<DnResourceUtil.JobTable.RecordCount; r++) {
					if (jobId == DnResourceUtil.JobTable.GetKey(r)) {
						name = Util.FindUIString((int)DnResourceUtil.JobTable[r, 0]);
						break;
					}
				}
				if (name == null) {
					name = jobId.ToString();
				}
				return name;
			}
		}

		public static int GetJobIconId(int jobId){
			int index = DnResourceUtil.JobTable.IndexOfKey(jobId);
			if (index >= 0) {
				return (int)DnResourceUtil.JobTable[index, 7];
			}
			return -1;
		}

		public static string FindUIString(long id) {
			if (id == UIString[0].id)
				return UIString[0].text;
			if (id == UIString[UIString.Length-1].id)
				return UIString[UIString.Length-1].text;
			return FindUIString(0, UIString.Length-1, id);
		}

		static string FindUIString(long l, long h, long id) {
			if (h-l<2) {
				return null;
			}
			long mid = (l+h)/2;
			if (id == UIString[mid].id)
				return UIString[mid].text;
			else if (id > UIString[mid].id) {
				return FindUIString(mid, h, id);
			} else
				return FindUIString(l, mid, id);
		}

		static Dntable fileTable;
		public static string GetFileName(int id) {
			if (fileTable == null) {
				FileHeader fh = mPakExplorer.FindFileHeaderWithPath(@"\resource\ext\filetable.dnt");
				fileTable = new Dntable();
				using (Stream s = mPakExplorer.GetFileStream(fh)) {
					fileTable.Load(s);
				}
			}
			if (id == fileTable.GetKey(0))
				return (string)fileTable[0, 0];
			if (id == fileTable.GetKey(fileTable.RecordCount-1))
				return (string)fileTable[fileTable.RecordCount-1, 0];
			return FindFileName(0, fileTable.RecordCount-1, id);
		}

		static string FindFileName(int l, int h, int id) {
			if (h-l<2) {
				return null;
			}
			int mid = (l+h)/2;
			if (id == fileTable.GetKey(mid))
				return (string)fileTable[mid, 0];
			else if (id > fileTable.GetKey(mid)) {
				return FindFileName(mid, h, id);
			} else
				return FindFileName(l, mid, id);
		}

		public static Dntable GetTable(string path) {
			Dntable table = mBufferdDntables[path] as Dntable;
			if (table == null) {
				FileHeader fh = mPakExplorer.FindFileHeaderWithPath(path);
				//bool done = false;
				//mPakExplorer.InvokeIfRequired(l => { fh=mPakExplorer.FindFileHeaderWithPath(path); done=true; });
				//while (!done)
				//	Thread.Sleep(10);
				if (fh != null) {
					table = new Dntable();
					table.Load(mPakExplorer.GetFileStream(fh));
					mBufferdDntables[path] = table;
				}
			}
			return table;
		}

		
		static UIString[] UIString {
			get {
				if (_uiString == null) {
					LoadUIString2();
				}
				return _uiString;
			}
		}
		static UIString[] _uiString;

		public static void LoadUIString() {
			XmlDocument doc = new XmlDocument();
			FileHeader fh = mPakExplorer.FindFileHeaderWithPath(@"\resource\uistring\uistring.xml");
			if (fh != null) {
				using (Stream s = mPakExplorer.GetFileStream(fh)) {
					doc.Load(s);
				}
			} else
                doc.Load(".\\Resoures\\uistring.xml");
			XmlNodeList list = doc.SelectNodes("/messages/message");
			_uiString = new UIString[list.Count];
			int i=0;
			foreach (XmlNode node in list) {
				_uiString[i] = new UIString();
				_uiString[i].id = int.Parse(node.Attributes.GetNamedItem("mid").InnerText);
				_uiString[i].text = node.InnerText;
				i++;
			}
#if DEBUG
			Process currentProcess = Process.GetCurrentProcess();
			System.Console.Write("UISting Count:"+_uiString.Length+" Mem:"+Util.FileSizeToString(currentProcess.WorkingSet64)+"\n");
#endif
		}

		public static void LoadUIString2() {
			XmlReader xr = null;
			FileHeader fh = mPakExplorer.FindFileHeaderWithPath(@"\resource\uistring\uistring.xml");
			Stream s = null;
			if (fh != null) {
				s = mPakExplorer.GetFileStream(fh);
				xr = XmlReader.Create(s);
			} else
                XmlReader.Create(".\\Resoures\\uistring.xml");
			List<UIString> list = new List<UIString>(130000);
			while (xr.Read()) {
				if (xr.NodeType == XmlNodeType.Element && xr.Name=="message") {
					UIString ustring = new UIString();
					ustring.id = int.Parse(xr.GetAttribute(0));
					ustring.text = xr.ReadElementContentAsString();
					list.Add(ustring);
				}
			}
			xr.Close();
			if (s != null)
				s.Close();
			list.CopyTo(_uiString = new UIString[list.Count]);
#if DEBUG
			Process currentProcess = Process.GetCurrentProcess();
			System.Console.Write("UISting Count:"+_uiString.Length+" Mem:"+Util.FileSizeToString(currentProcess.WorkingSet64)+"\n");
#endif
		}

		static string[] stringBuff = new string[20];
		public static string parseParams(string str) {
			int paramsCount = 0;
			int len = str.Length;
			for (int i=0; i<len; i++) {
				if (str[i] == '{') {
					int id = 0;
					while (++i<len) {
						if (str[i]=='}') {
							stringBuff[paramsCount++] = FindUIString(id);
							break;
						} else {
							id = id*10 + (str[i]-'0');
						}
					}
				}
			}
			if (paramsCount==0)
				return null;
			//string[] paramsStr = new string[paramsCount];
			string s = "";
			for (int i=0; i<paramsCount; i++) {
				s += stringBuff[i];
			}
			return s;
			//return paramsStr;
		}

		private static List<int> JobParent {
			get { 
				if (mJobParent == null) {
					Dntable jobTable = DnResourceUtil.JobTable;
					int max = 0;
					for (int i=0; i<jobTable.RecordCount; i++) {
						if (jobTable.GetKey(i) > max)
							max = jobTable.GetKey(i);
					}
					mJobParent = new List<int>(max+1);
					for (int i=0; i<=max; i++) {
						mJobParent.Add(0);
					}
					for (int i=0; i<jobTable.RecordCount; i++) {
						mJobParent[jobTable.GetKey(i)] = (int)jobTable[i, 5];
					}
				}
				return mJobParent;
			}
		}

		public static int GetParentJob(int job) {
			return JobParent[job];
		}

		public static bool JobCanAccepet(int job, int jobLimit) {
			List<int> jobParent = JobParent;
			while (jobLimit > 0 && jobLimit != job) {
				if ((job = jobParent[job]) <= 0) {
					return false;	// 不匹配且没有父职业
				}
			}
			return true;	// 无职业限制或者正确匹配
		}

		public static bool JobCanAccepet(int job, string jobLimit) {
			string[] jobLimits = jobLimit.Split(';');
			foreach (string j in jobLimits) {
				if (j == "")
					continue;
				try {
					int jobId = int.Parse(j);
					if (JobCanAccepet(job, jobId))
						return true;
				} catch (FormatException) {
				}
			}
			return false;	// 无职业限制或者正确匹配
		}

		public static string[] parseParams2(string nameParamsID, out int length) {
			int paramsCount = 0;
			int len = nameParamsID.Length;
			for (int i=0; i<len; i++) {
				if (nameParamsID[i] == '{') {
					int id = 0;
					while (++i<len) {
						if (nameParamsID[i]=='}') {
							stringBuff[paramsCount++] = FindUIString(id);
							break;
						} else {
							id = id*10 + (nameParamsID[i]-'0');
						}
					}
				}
			}
			length = paramsCount;
			stringBuff[paramsCount] = "";
			return stringBuff;
		}

		public static string[] parseParams2(string nameParamsID) {
			int paramsCount = 0;
			int len = nameParamsID.Length;
			for (int i=0; i<len; i++) {
				if (nameParamsID[i] == '{') {
					int id = 0;
					while (++i<len) {
						if (nameParamsID[i]=='}') {
							stringBuff[paramsCount++] = FindUIString(id);
							break;
						} else {
							id = id*10 + (nameParamsID[i]-'0');
						}
					}
				}
			}
			string[] paramsStr = new string[paramsCount];
			for (int i=0; i<paramsCount; i++) {
				paramsStr[i] = stringBuff[i];
			}
			return paramsStr;
		}

		/// <summary>
		/// 以非数字字符作为分割符，参数数组
		/// </summary>
		/// <param name="nameParamsID"></param>
		/// <returns></returns>
		public static string[] parseParams3(string nameParamsID) {
			int paramsCount = 0;
			int len = nameParamsID.Length;
			for (int i=0; i<len; i++) {
				if ('0'<=nameParamsID[i] && nameParamsID[i]<='9') {
					int id = nameParamsID[i]-'0';
					while (++i<len) {
						if ('0'<=nameParamsID[i] && nameParamsID[i]<='9')
							id = id*10 + (nameParamsID[i]-'0');
						else
							break;
					}
					stringBuff[paramsCount++] = FindUIString(id);
				}
			}
			string[] paramsStr = new string[paramsCount];
			for (int i=0; i<paramsCount; i++) {
				paramsStr[i] = stringBuff[i];
			}
			return paramsStr;
		}

		public static string ParseDnString(int strId, string param) {
			if (strId == 0) {
				return "";
			}
			string s = Util.FindUIString(strId);
			int c;
			if (param != "") {
				try {
					s = string.Format(s, Util.parseParams3(param, out c));
				} catch (FormatException) { }
			}
			return s;
		}

		/// <summary>
		/// 以非数字字符作为分割符，参数列表的缓冲区
		/// </summary>
		/// <param name="nameParamsID"></param>
		/// <returns></returns>
		public static string[] parseParams3(string nameParamsID, out int count) {
			int paramsCount = 0;
			int len = nameParamsID.Length;
			char lastNotDigitChar = ',';
			for (int i=0; i<len; i++) {
				char c = nameParamsID[i];
				if ('0'<=c && c<='9') {	// 第一个数字
					int id = c-'0';
					while (++i<len) {
						c = nameParamsID[i];
						if ('0'<=c && c<='9')
							id = id*10 + (c-'0');
						else		
							break;
					}
					// 值解析完毕，定位到了非数字或是结尾
					if (lastNotDigitChar == '{')
						stringBuff[paramsCount++] = FindUIString(id);
					else
						stringBuff[paramsCount++] = id.ToString();
				}
				lastNotDigitChar = c;
			}
			stringBuff[paramsCount] = "";
			count = paramsCount;
			return stringBuff;
		}

		static Image[] mItemImagesBuffer = new Image[400];
		static Image[] mSkillImagesBuffer = new Image[100];
		static Image mImageItemBox;
		static Image mImageItemButtons;

		private static Image GetItemIconImage(int index) {
			if (index<0 || mItemImagesBuffer.Length<=index)
				return null;
			if (mItemImagesBuffer[index] != null) {
				return mItemImagesBuffer[index];
			} else {
				return mItemImagesBuffer[index] = LoadItemIconImage(index);
			}
		}


		private static Image LoadItemIconImage(int index) {
			string path = null;
			if(index <= 9)
				path = @"\resource\ui\mainbar\itemicon0"+(index+1)+".dds";
			else {
				path = @"\resource\ui\mainbar\itemicon"+(index+1)+".dds";
			}
			FileHeader fh = mPakExplorer.FindFileHeaderWithPath(path);
			if (fh != null) {
				return new DDSImage(mPakExplorer.GetFileStream(fh)).BitmapImage;
			}
			return null;
		}

		private static Image GetSkillIconImage(int index) {
			if (index<0 || mSkillImagesBuffer.Length<=index)
				return null;
			if (mSkillImagesBuffer[index] != null) {
				return mSkillImagesBuffer[index];
			} else {
				return mSkillImagesBuffer[index] = LoadSkillIconImage(index);
			}
		}

		private static Image JobIconPvp {
			get {
				if (mJobIconPvp == null) {
					FileHeader fh = mPakExplorer.FindFileHeaderWithPath(@"\resource\ui\mainbar\jobicon_pvp.dds");
					if (fh != null) {
						mJobIconPvp = new DDSImage(mPakExplorer.GetFileStream(fh)).BitmapImage;
					}
				}
				return mJobIconPvp;
			}
		}


		private static Image LoadSkillIconImage(int index) {
			string path = null;
			if (index <= 9)
				path = @"\resource\ui\mainbar\skillicon0"+(index+1)+".dds";
			else {
				path = @"\resource\ui\mainbar\skillicon"+(index+1)+".dds";
			}
			FileHeader fh = mPakExplorer.FindFileHeaderWithPath(path);
			if (fh != null) {
				return new DDSImage(mPakExplorer.GetFileStream(fh)).BitmapImage;
			}
			return null;
		}

		public static void DrawJobIconPvP(Graphics g, Rectangle dest, int jobIconId) {
			if(JobIconPvp != null){
				int x = jobIconId%9*28;
				int y = jobIconId/9*28;
				g.DrawImage(JobIconPvp, dest, new Rectangle(x, y, 28, 28), GraphicsUnit.Pixel);
			}
		}

		public static void DrawItemImage(int imageIconIndex, RectangleF dest, Graphics g) {
			int imageIndex = imageIconIndex/200;
			int indexX = imageIconIndex%200%10;
			int indexY = imageIconIndex%200/10;
			Image image = GetItemIconImage(imageIndex);
			int v = 50;
			int h = 50;
			if (image != null)
				g.DrawImage(image, new RectangleF(dest.X, dest.Y, dest.Width, dest.Height), new RectangleF(indexX*h, indexY*v, h, v), GraphicsUnit.Pixel);
		}

		public static void DrawSkillImage(int imageIconIndex, RectangleF dest, Graphics g) {
			int imageIndex = imageIconIndex/200;
			int indexX = imageIconIndex%200%10;
			int indexY = imageIconIndex%200/10;
			Image image = GetSkillIconImage(imageIndex);
			int v = 50;
			int h = 50;
			if (image != null)
				g.DrawImage(image, new RectangleF(dest.X, dest.Y, dest.Width, dest.Height), new RectangleF(indexX*h, indexY*v, h, v), GraphicsUnit.Pixel);
		}

		public static string GetExchangeName(ItemType exchangType) {
			if (mExchangeTable == null) {
				mExchangeTable = new Dntable();
				mExchangeTable = Util.GetTable(@"\resource\ext\exchange.dnt");
			}
			for (int r=0; r<mExchangeTable.RecordCount; r++) {
				if ((ItemType)mExchangeTable[r, 3] == exchangType) {
					return Util.FindUIString((int)mExchangeTable[r, 0]);
				}
			}
			return null;
		}

		public static void DrawItemBox(int itemRank, RectangleF dest, Graphics g) {
			if (mImageItemBox == null) {
				FileHeader fh = mPakExplorer.FindFileHeaderWithPath(@"\resource\ui\common\itemchoicedlg.dds");
				if (fh != null) {
					mImageItemBox = new DDSImage(mPakExplorer.GetFileStream(fh)).BitmapImage;
				}
			}
			if (mImageItemBox != null) {
				int offsetX = 0;
				int offsetY = 0;
				switch (itemRank) {
				case 0:
					offsetX = 0;
					offsetY = 373;
					break;
				case 1:
					offsetX = 196;
					offsetY = 181;
					break;
				case 2:
					offsetX = 196;
					offsetY = 233;
					break;
				case 3:
					offsetX = 248;
					offsetY = 233;
					break;
				case 4:
					offsetX = 248;
					offsetY = 285;
					break;
				case 5:
					offsetX = 248;
					offsetY = 129;
					break;
				}
				
				int v = 50;
				int h = 50;
				g.DrawImage(mImageItemBox, new RectangleF(dest.X, dest.Y, dest.Width, dest.Height), new RectangleF(offsetX, offsetY, h, v), GraphicsUnit.Pixel);
			}
			
		}

		public static void DrawItemTags(IItem item, RectangleF dest, Graphics g) {
			IUserItem userItem = item as IUserItem;
			if (userItem != null) {
				if (userItem.AppliedImprintingSlot != null && userItem.AppliedImprintingSlot.Item != null) {
					if (mImageItemButtons == null) {
						mImageItemButtons = Properties.Resources.uit_itemslotbutton;
					}
					g.DrawImage(mImageItemButtons, dest, new Rectangle(103, 156, 52, 52), GraphicsUnit.Pixel);
				}
			}
			if (item.isCash) {
				if (mImageItemButtons == null) {
					mImageItemButtons = Properties.Resources.uit_itemslotbutton;
				}
				g.DrawImage(mImageItemButtons, dest, new Rectangle(260, 103, 52, 52), GraphicsUnit.Pixel);
			}
		}

		

		private static string[] mRankText = new string[]{
			"D级","C级","B级","A级","S级","L级","传说"
		};

		private static Color[] mRankColor = new Color[]{
			Color.WhiteSmoke, Color.ForestGreen, Color.RoyalBlue, Color.SandyBrown, Color.MediumOrchid, Color.DeepPink, Color.Yellow
		};

		public static Color GetRankColor(int rank) {
			if (rank>=0 && rank<mRankColor.Length)
				return mRankColor[rank];
			else
				return Color.OrangeRed;
		}

		public static string GetRankText(int rank) {
			if (rank>=0 && rank<mRankText.Length)
				return mRankText[rank];
			else 
				return rank.ToString();
		}


		public static void LoadTables() {
			ProgressForm2 progress = new ProgressForm2();
			new Thread(delegate() {
				DnResourceUtil.LoadAllTables();
				lock (progress) {
					progress.InvokeIfRequired(l => { l.Done = true; l.Close(); });
				}
			}).Start();
			if (!progress.Done)
				progress.ShowDialog();
				
		}

		private static Image[] mJewelTypeImages = new Image[]{
				new DDSImage((byte[])Properties.Resources.uit_re_itemjewelslot02).BitmapImage,
				new DDSImage((byte[])Properties.Resources.uit_re_itemjewelslot01).BitmapImage,
				new DDSImage((byte[])Properties.Resources.uit_re_itemjewelslot03).BitmapImage,
				new DDSImage((byte[])Properties.Resources.uit_re_itemjewelslot04).BitmapImage,
			};

		public static Image GetJewelTypeImage(int type) {
			return mJewelTypeImages[type-1];
		}

	}

	public class PropertySumer {
		Property[] mSum = new Property[(int)PropertyID.ID_MAX];

		public void AddProperty(Property prop) {
			if (mSum[(int)prop.id] == null) {
				mSum[(int)prop.id] = new Property();
				mSum[(int)prop.id].id = prop.id;
			}
			mSum[(int)prop.id].value += prop.value;
		}

		public void AddItem(IUserItem item) {
			foreach (Property prop in item) {
				AddProperty(prop);
			}
		}

		public List<Property> ToList() {
			List<Property> list = new List<Property>();
			for (int i=0; i<mSum.Length; i++) {
				if (mSum[i] != null) {
					list.Add(mSum[i]);
				}
			}
			return list;
		}
	}
}

// 四件套泳装		攻击指数
// 2魔			294958.206750512
// 2火			292010.52360855
// 1.6魔 2.1火	293330.12519154
// 2.1魔 1.6火	293727.773795798

// 7件套S装
// 2魔			302142.172736183
// 2火			299232.062196612
// 1.6魔 2.1火	300536.059526995
// 2.1魔 1.6火	300928.434270322
